#ifndef EASYASIO_BASE_LOGGER_H_
#define EASYASIO_BASE_LOGGER_H_

#include <string>
#include <list>
#include <thread>
#include <mutex>
#include <condition_variable>
#include "format.h"

namespace easyasio {
namespace base {

const char* getBaseName(const char* filename);

class LogFile;

class Logger
{
public:
    enum LogLevel 
    { 
        LOGLEVEL_TRACE, 
        LOGLEVEL_DEBUG, 
        LOGLEVEL_INFO, 
        LOGLEVEL_WARN, 
        LOGLEVEL_ERROR, 
        LOGLEVEL_FATAL, 
        LOGLEVEL_NUM_LOG_LEVELS
    };

    static Logger& instance();
    void setBaseName(const std::string& path);

    void setLevel(LogLevel l) { level_ = l; }
    LogLevel level()const { return level_; }
    void output(const std::string& s, LogLevel level);

private:
    Logger(const Logger&) = delete;
    Logger operator=(const Logger&) = delete;
    Logger();
    ~Logger();
    void start();
    void stop();

    std::string formateMsg(const std::string& s, LogLevel level);
    void writeMsgLoop();

private:
    std::shared_ptr<std::thread> msg_writer_;
    std::list<std::string> msgs_;
    std::mutex msgs_mutex_;
    std::condition_variable msgs_condition_variable_;
    volatile bool runing_;

    LogLevel level_;
    std::string fullbasepath_;
    std::shared_ptr<LogFile> logfile_;
};

} 
}
 
#define LOG_TRACE(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_TRACE) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__, getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_TRACE); \
    } while (0) 

#define LOG_DEBUG(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_DEBUG) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__, getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_DEBUG); \
    } while (0) 

#define LOG_INFO(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_INFO) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__, getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_INFO); \
    } while (0) 

#define LOG_WARN(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_WARN) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__,  getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_WARN); \
    } while (0) 

#define LOG_ERROR(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_ERROR) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__, getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_ERROR); \
    } while (0) 

#define LOG_FATAL(log_fmt, ...) \
    do{ using namespace easyasio::base; \
        if (Logger::instance().level() > Logger::LOGLEVEL_FATAL) break; \
        std::string fstr = fmt::format(log_fmt" {}:{}", ##__VA_ARGS__, getBaseName(__FILE__), __LINE__);\
        Logger::instance().output(std::move(fstr), Logger::LOGLEVEL_FATAL); \
    } while (0) 

#endif//EASYASIO_BASE_LOGGER_H_